#include "general.h"

/* Inicia a precisão */
void init_prec()
{
    mpf_init_set_ui(prec, 10);
    mpf_pow_ui(prec, prec, NDIGITS); mpf_ui_div(prec, 1, prec); // prec = 1/(10^PREC)
}

/* Limpa a precisão */
void clear_prec()
{
    mpf_clear(prec);
}

/* Executa um método para calcular Pi */
void execute_method(int how, int method, mpf_t pi, long *it, int *time, char *file)
{
    struct timeval start, end; /* Variáveis para guardar o tempo atual */

    gettimeofday(&start, NULL); /* Pega o tempo antes da execução do algoritmo */

    /* Verifica o método */
    switch(method)
    {
        /* Gauss-Legendre */
        case GAUSS_LEGENDRE:
            if(how == SEQUENTIALLY)
            {
                gauss_legendre(pi, it);
                strcpy(file, GAUSS_LEGENDRE_FILE);
            }
            else
            {
                gauss_legendre_conc(pi, it);
                strcpy(file, GAUSS_LEGENDRE_CONC_FILE);
            }
            break;

        /* Borwein */
        case BORWEIN:
            if(how == SEQUENTIALLY)
            {
                borwein(pi, it);
                strcpy(file, BORWEIN_FILE);
            }
            else
            {
                borwein_conc(pi, it);
                strcpy(file, BORWEIN_CONC_FILE);
            }
            break;

        /* Monte Carlo */
        case MONTE_CARLO:
            if(how == SEQUENTIALLY)
            {
                monte_carlo(pi, it);
                strcpy(file, MONTE_CARLO_FILE);
            }
            else
            {
                monte_carlo_conc(pi, it);
                strcpy(file, MONTE_CARLO_CONC_FILE);
            }
            break;
    }

    gettimeofday(&end, NULL); /* Pega o tempo depois da execução do algoritmo */
    *time = end.tv_sec - start.tv_sec; /* time será diferença dada em segundos dos tempos */

    /* Salva o valor do Pi calculado no arquivo de saída */
    save_pi(pi, file);
}

/* Verifica a convergência de dois valores utilizando a variável prec */
int has_converged(mpf_t a0, mpf_t a1)
{
    mpf_t aux;
    int ret;

    mpf_init(aux);

    /* aux será a diferença absoluta entre os dois parâmetros: aux = |a1-a0| */
    mpf_sub(aux, a1, a0); mpf_abs(aux, aux);
    
    /* Compara com a precisão e, se for menor, garantimos convergência */
    ret = (mpf_cmp(aux, prec) <= 0);

    mpf_clear(aux);

    return ret;
}

/* Verifica a convergência de dois valores utilizando precisão de 5 casas decimais */
int has_kind_of_converged(mpf_t a0, mpf_t a1)
{
    mpf_t aux, lprec;
    int ret;

    mpf_init(aux);
    mpf_init_set_d(lprec, 0.00001);

    /* aux será a diferença absoluta entre os dois parâmetros: aux = |a1-a0| */
    mpf_sub(aux, a1, a0); mpf_abs(aux, aux);
    
    /* Compara com a precisão e, se for menor, garantimos convergência */
    ret = (mpf_cmp(aux, lprec) <= 0);

    mpf_clear(aux);

    return ret;
}

/* Salva o valor de Pi em um arquivo */
void save_pi(mpf_t pi, char *file)
{
    FILE *fp = fopen(file, "w"); /* Abre o arquivo */
    mpf_out_str(fp, 10, NDIGITS, pi); /* Imprime com precisão 10^7 dígitos*/
    fclose(fp); /* Fecha o arquivo */
}
